package com.bjy.qa.service.functionaltest.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bjy.qa.dao.functionaltest.*;
import com.bjy.qa.dao.performancetest.PerfTestSuiteTestScriptDao;
import com.bjy.qa.dao.performancetest.PerfTestSuiteTestScriptGeneratorDao;
import com.bjy.qa.dao.performancetest.TestScriptDao;
import com.bjy.qa.entity.functionaltest.*;
import com.bjy.qa.entity.performancetest.PerfTestSuiteTestScript;
import com.bjy.qa.entity.performancetest.PerfTestSuiteTestScriptGenerator;
import com.bjy.qa.enumtype.CatalogType;
import com.bjy.qa.enumtype.SortType;
import com.bjy.qa.exception.MyException;
import com.bjy.qa.service.functionaltest.ICatalogService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

@Service
public class CatalogService extends ServiceImpl<CatalogDao, Catalog> implements ICatalogService {
    @Resource
    private CatalogDao catalogDao;

    @Resource
    private TestCaseDao testCaseDao;

    @Resource
    private StepDao stepDao;

    @Resource
    private StepService stepService;

    @Resource
    private TestSuiteTestCaseDao testSuiteTestCaseDao;

    @Resource
    private ApiDao apiDao;

    @Resource
    private ApiParamService apiParamService;

    @Resource
    TestScriptDao testScriptDao;

    @Resource
    PerfTestSuiteTestScriptDao perfTestSuiteTestScriptDao;

    @Resource
    PerfTestSuiteTestScriptGeneratorDao perfTestSuiteTestScriptGeneratorDao;

    @Override
    public List<Catalog> listCatalogByParentId(CatalogType type, Long projectId, Long parentId) {
        return catalogDao.listCatalogByParentId(type, projectId, parentId);
    }

    @Override
    public Catalog selectByName(CatalogType type, Long projectId, Long parentId, String name) {
        return catalogDao.selectByName(type, projectId, parentId, name);
    }

    @Override
    public List<Tree> listTreeContent(CatalogType type, Long projectId, Long parentId) {
        if (type == CatalogType.INTERFACE_HTTP || type == CatalogType.INTERFACE_WEB_SOCKET || type == CatalogType.INTERFACE_DATA_CHANNEL || type == CatalogType.INTERFACE_DATABASE || type == CatalogType.INTERFACE_SSH) {
            return catalogDao.listApiTreeContent(type, type, projectId, parentId);
        } else if (type == CatalogType.TEST_CASE || type == CatalogType.INTERFACE_TEST_CASE || type == CatalogType.PUBLIC_STEP) {
            return catalogDao.listTestCaseTreeContent(type, type, projectId, parentId);
        } else if (type == CatalogType.PERFORMANCE_TEST_SCRIPT) {
            return catalogDao.listTestScriptTreeContent(type, projectId, parentId);
        } else {
            throw new MyException("查询 tree 上某个节点的所有数据列表失败：type 未知，type: " + type + ", type.value: " + type.getValue());
        }
    }

    @Override
    public List<FullTree> listTree(CatalogType type, CatalogType caseType, Long projectId, Long parentId) {
        if (type == CatalogType.INTERFACE_HTTP || type == CatalogType.INTERFACE_WEB_SOCKET || type == CatalogType.INTERFACE_DATA_CHANNEL || type == CatalogType.INTERFACE_DATABASE || type == CatalogType.INTERFACE_SSH) {
            return recursiveQueryTree("ft_api", type, type, null, projectId, parentId);
        } else if (type == CatalogType.TEST_CASE || type == CatalogType.INTERFACE_TEST_CASE || type == CatalogType.PUBLIC_STEP) {
            return recursiveQueryTree("ft_test_case", type, type, caseType, projectId, parentId);
        } else if (type == CatalogType.PERFORMANCE_TEST_SCRIPT) {
            return recursiveQueryTree("pt_test_script", type, type, caseType, projectId, parentId);
        } else {
            throw new MyException("查询 tree 上某个type所有数据列表失败：type 未知，type: " + type + ", type.value: " + type.getValue());
        }
    }

    private List<FullTree> recursiveQueryTree(String elementTableName, CatalogType catalogType, CatalogType elementType, CatalogType caseType, Long projectId, Long parentId) {
        List<FullTree> fullTrees = null;
        if ("ft_api".equals(elementTableName)) {
            fullTrees = catalogDao.listApiTree(catalogType, elementType, caseType, projectId, parentId);
        } else if ("ft_test_case".equals(elementTableName)) {
            fullTrees = catalogDao.listTestCaseTree(catalogType, elementType, caseType, projectId, parentId);
        } else if ("pt_test_script".equals(elementTableName)) {
            fullTrees = catalogDao.listTestScriptTree(catalogType, projectId, parentId);
        } else {
            throw new MyException("查询 tree 上某个type所有数据列表失败：elementTableName 未知，elementTableName: " + elementTableName);
        }

        for (FullTree fullTree : fullTrees) {
            if (fullTree.getIsCatalog()) {
                fullTree.setChildren(recursiveQueryTree(elementTableName, catalogType, elementType, caseType, projectId, fullTree.getId()));
            }
        }
        return fullTrees;
    }

    @Override
    public List<FullTree> listTreeCatalog(CatalogType type, CatalogType caseType, Long projectId, Long parentId) {
        if (type == CatalogType.INTERFACE_HTTP || type == CatalogType.INTERFACE_WEB_SOCKET || type == CatalogType.INTERFACE_DATA_CHANNEL || type == CatalogType.INTERFACE_DATABASE || type == CatalogType.INTERFACE_SSH) {
            return recursiveQueryTreeCatalog(type, type, null, projectId, parentId);
        } else if (type == CatalogType.TEST_CASE || type == CatalogType.INTERFACE_TEST_CASE || type == CatalogType.PUBLIC_STEP) {
            return recursiveQueryTreeCatalog(type, type, caseType, projectId, parentId);
        } else {
            throw new MyException("查询 tree 上某个type所有数据列表失败：type 未知，type: " + type + ", type.value: " + type.getValue());
        }
    }

    /**
     * 递归查询 tree 完整路径 - 只包含目录
     * @param catalogType 分类类型 枚举类
     * @param elementType 用例类型 枚举类
     * @param caseType 用例类型 枚举类
     * @param projectId 项目ID
     * @param parentId tree 节点 ID
     * @return
     */
    private List<FullTree> recursiveQueryTreeCatalog(CatalogType catalogType, CatalogType elementType, CatalogType caseType, Long projectId, Long parentId) {
        List<FullTree> fullTrees = catalogDao.listTestCaseTreeCatalog(catalogType, elementType, caseType, projectId, parentId);
        for (FullTree fullTree : fullTrees) {
            if (fullTree.getIsCatalog()) {
                fullTree.setChildren(recursiveQueryTreeCatalog(catalogType, elementType, caseType, projectId, fullTree.getId()));
            }
        }
        return fullTrees;
    }

    @Override
    public Integer treeSort(Long projectId, TreeSort treeSort) {
        Tree source = treeSort.getSource(); // 源
        Tree target = treeSort.getTarget(); // 目标
        SortType sortType = treeSort.getSortType(); // 排序类型

        // 根据 CatalogType 类型，确定元素表名称。后续更新数据的时候用的到
        String eTableName;
        CatalogType cType = treeSort.getType();
        if (cType == CatalogType.INTERFACE_HTTP || cType == CatalogType.INTERFACE_WEB_SOCKET || cType == CatalogType.INTERFACE_DATA_CHANNEL || cType == CatalogType.INTERFACE_DATABASE || cType == CatalogType.INTERFACE_SSH) {
            eTableName = "ft_api";
        } else if (cType == CatalogType.TEST_CASE) {
            eTableName = "ft_test_case";
        } else if (cType == CatalogType.INTERFACE_TEST_CASE) {
            eTableName = "ft_test_case";
        } else if (cType == CatalogType.PUBLIC_STEP) {
            eTableName = "ft_test_case";
        } else {
            throw new MyException("查询 tree 上某个节点的所有数据列表失败：type 未知，type: " + cType + ", type.value: " + cType.getValue());
        }

        // 目标节点内-子节点
        if (sortType == SortType.INNER) {
            if (!target.getIsCatalog()) {
                throw new MyException("SortType 为 INNER（子节点）时，目标节点只能是 目录");
            }

            // 构造
            List<Tree> tempTreeList = new LinkedList<>();
            Tree tempTree = new Tree();
            tempTree.setId(source.getId());
            tempTree.setParentId(target.getId());
            tempTreeList.add(tempTree);

            if (source.getIsCatalog()) { // 目录 拖动到 目录
                return catalogDao.sortCatalog(tempTreeList);
            } else { // 元素 拖动到 目录
                return catalogDao.sortElement(eTableName, tempTreeList);
            }
        }

        // 生成新的排序后的列表
        List<Tree> tempTreeList = new LinkedList<>(); // 保存排序后的数据
        // 查找待排序列表
        List<Tree> trees;
        if (source.getIsCatalog() && target.getIsCatalog()) { // 目录之间排序
            trees = catalogDao.listSubCatalogs(cType, projectId, target.getParentId());
        } else { // 元素之间排序
            trees = catalogDao.listSubElements(eTableName, cType, projectId, target.getParentId());
        }
        // 遍历待排序列表，修改 sort 后放到新的排序后的列表中
        int j = 0;
        for (int i = 0; i < trees.size(); i++) {
            if (trees.get(i).getId().equals(target.getId())) { // 是目标数据，按照 BEFORE 或 AFTER 放到新的排序后的列表中
                j = 1;
                Tree tempTree = new Tree();
                tempTree.setId(source.getId());
                tempTree.setParentId(target.getParentId());
                if (sortType == SortType.BEFORE) {
                    // 先放 目标数据
                    tempTree.setSort(i + 1);
                    tempTreeList.add(tempTree);
                    // 再放 源数据
                    trees.get(i).setSort(i + j + 1);
                    tempTreeList.add(trees.get(i));
                } else if (sortType == SortType.AFTER) {
                    // 先放 源数据
                    trees.get(i).setSort(i + 1);
                    tempTreeList.add(trees.get(i));
                    // 再放 目标数据
                    tempTree.setSort(i + j + 1);
                    tempTreeList.add(tempTree);
                }
            } else { // 不是目标数据，直接放到新的排序后的列表中
                trees.get(i).setSort(i + j + 1); // 查出来的重新排序
                if (trees.get(i).getId().equals(source.getId())) {
                    j = 0;
                } else {
                    tempTreeList.add(trees.get(i));
                }
            }
        }
        // 如果 目标 父分类下没有元素，将 源 添加到第一个
        if (trees.size() == 0) {
            Tree tempTree = new Tree();
            tempTree.setId(source.getId());
            tempTree.setParentId(target.getParentId());
            tempTree.setSort(1);
            tempTreeList.add(tempTree);
        }

        // 更新 数据
        if (source.getIsCatalog() && target.getIsCatalog()) { // 目录 拖动到 目录，更新 目录 表
            return catalogDao.sortCatalog(tempTreeList);
        } else { // 更新 元素 表
            return catalogDao.sortElement(eTableName, tempTreeList);
        }
    }

    @Override
    public List<Tree> getElementParent(CatalogType type, Long id) {
        if (type == CatalogType.INTERFACE_HTTP || type == CatalogType.INTERFACE_WEB_SOCKET || type == CatalogType.INTERFACE_DATA_CHANNEL || type == CatalogType.INTERFACE_DATABASE || type == CatalogType.INTERFACE_SSH) {
            Tree element = catalogDao.getElement("ft_api", id);
            List<Tree> list = new ArrayList<>();
            if (element != null) {
                list = catalogDao.getElementParent(element.getParentId());
                Collections.reverse(list);
                list.add(element);
            }
            return list;
        } else if (type == CatalogType.TEST_CASE || type == CatalogType.INTERFACE_TEST_CASE || type == CatalogType.PUBLIC_STEP) {
            Tree element = catalogDao.getElement("ft_test_case", id);
            List<Tree> list = new ArrayList<>();
            if (element != null) {
                list = catalogDao.getElementParent(element.getParentId());
                Collections.reverse(list);
                list.add(element);
            }
            return list;
        } else {
            throw new MyException("根据当前元素id递归查找父分类列表失败：当前元素 type 未知，type: " + type + ", type.value: " + type.getValue());
        }
    }

    @Override
    public Catalog selectById(Long id) {
        return catalogDao.selectById(id);
    }

    @Override
    public Catalog add(Catalog catalog) {
        catalogDao.insert(catalog);
        return catalog;
    }

    @Override
    public Catalog update(Catalog catalog) {
        // 更新数据需要清除 UpdatedTime 和 CreatedTime，否则不会自动更新修改时间
        catalog.setUpdatedAt(null);
        catalog.setCreatedAt(null);

        catalogDao.updateById(catalog);
        return catalog;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int delete(CatalogType type, Long projectId, Long id) {
        //  1、遍历tree
        //  2、删除每个目录下的 testCase
        //  3、删除 testCase 的同时记住 testCaseId，再用这些 id 将 testSuiteCase 表中的测试套件关联测试用例的数据删除
        //  4、删除目录
        List<Tree> listTreeContent;
        List<Tree> treeList;
        List<Long> caseList = new ArrayList<>();
        List<Long> catalogList = new ArrayList<>();
        if (type == CatalogType.INTERFACE_HTTP || type == CatalogType.INTERFACE_WEB_SOCKET || type == CatalogType.INTERFACE_DATA_CHANNEL || type == CatalogType.INTERFACE_DATABASE || type == CatalogType.INTERFACE_SSH) {
            listTreeContent = catalogDao.listApiTreeContent(type, type, projectId, id);
            if (listTreeContent.size() != 0) {
                throw new MyException("分类删除失败，请在 “" + type.getName() + "” 中检查该分类下是否有子节点。");
            }
        } else if (type == CatalogType.TEST_CASE || type == CatalogType.INTERFACE_TEST_CASE || type == CatalogType.PUBLIC_STEP) {
            treeList = findTreeContent("ft_test_case", type, type, projectId, id);
            if (treeList.size() != 0){
                for (Tree tree: treeList){
                    if (tree.getIsCatalog()==true){
                        catalogList.add(tree.getId());
                    }else{
                        caseList.add(tree.getId());
                    }
                }
            }

            if (caseList.size() != 0) {
                stepDao.updateByCidList(caseList); // 删除 step
                testSuiteTestCaseDao.updateByCidList(caseList); // 解除套件和用例之间的关联关系
                testCaseDao.deleteBatchIds(caseList); // 删除 case
            }

            // 删除子目录
            if (catalogList.size() != 0) {
                for (Long catalogId : catalogList) {
                    this.delete(type, projectId, catalogId);
                }
                catalogDao.deleteBatchIds(catalogList);
            }
        } else if (type == CatalogType.PERFORMANCE_TEST_SCRIPT) {
            treeList = findTreeContent("pt_test_script", type, type, projectId, id);
            if (treeList.size() != 0){
                for (Tree tree: treeList){
                    if (tree.getIsCatalog()==true){
                        catalogList.add(tree.getId());
                    }else{
                        caseList.add(tree.getId());
                    }
                }
            }

            if (caseList.size() != 0) {
                // TODO: 2024/4/15 临时去掉（过一段时间如果没问题，彻底删除）
//                // 查询 这些要删除的 性能测试脚本，都在哪些 性能测试套件 中对应 性能测试脚本 ids
//                QueryWrapper<PerfTestSuiteTestScript> perfTestSuiteTestScriptQueryWrapper = new QueryWrapper<>();
//                perfTestSuiteTestScriptQueryWrapper.in("test_script_id", caseList);
//                List<PerfTestSuiteTestScript> perfTestSuiteTestScriptList = perfTestSuiteTestScriptDao.selectList(perfTestSuiteTestScriptQueryWrapper);
//                List<Long> perfTestSuiteTestScriptIds = new ArrayList<>();
//                for (PerfTestSuiteTestScript temp : perfTestSuiteTestScriptList) {
//                    perfTestSuiteTestScriptIds.add(temp.getId());
//                }
//
//                if (perfTestSuiteTestScriptIds.size() > 0) {
//                    // 删除 性能测试套件 中 性能测试脚本 对应 压力机 Entity
//                    QueryWrapper<PerfTestSuiteTestScriptGenerator> perfTestSuiteTestScriptGeneratorQueryWrapper = new QueryWrapper<>();
//                    perfTestSuiteTestScriptQueryWrapper.in("perf_test_suite_test_script_id", perfTestSuiteTestScriptIds);
//                    perfTestSuiteTestScriptGeneratorDao.delete(perfTestSuiteTestScriptGeneratorQueryWrapper);
//
//                    perfTestSuiteTestScriptDao.deleteBatchIds(perfTestSuiteTestScriptIds); // 删除 性能测试套件 对应 性能测试脚本
//                }

                testScriptDao.deleteBatchIds(caseList); // 删除 性能测试脚本 test_script
            }

            // 删除子目录
            if (catalogList.size() != 0) {
                for (Long catalogId : catalogList) {
                    this.delete(type, projectId, catalogId);
                }
                catalogDao.deleteBatchIds(catalogList);
            }
        } else {
            throw new MyException("查询 tree 上某个节点的所有数据列表失败：type 未知，type: " + type + ", type.value: " + type.getValue());
        }
        return catalogDao.deleteById(id);
    }

    @Override
    public Tree copy(CatalogType type, Long projectId, Long id) {
        // 复制分类
        Catalog catalog = catalogDao.selectById(id);
        if (catalog == null) {
            throw new MyException("复制分类失败，分类不存在或已删除！ 分类 ID: " + id);
        }
        catalog.setName(catalog.getName() + " - copy");
        catalog.setId(null);
        catalog.setUpdatedAt(null);
        catalog.setCreatedAt(null);
        catalogDao.insert(catalog);

        // 递归查询分类下的元素
        List<FullTree> sourceTreeList;
        if (type == CatalogType.INTERFACE_HTTP || type == CatalogType.INTERFACE_WEB_SOCKET || type == CatalogType.INTERFACE_DATA_CHANNEL || type == CatalogType.INTERFACE_DATABASE || type == CatalogType.INTERFACE_SSH) {
            sourceTreeList = recursiveQueryTree("ft_api", type, type, null, projectId, id);
        } else if (type == CatalogType.TEST_CASE || type == CatalogType.INTERFACE_TEST_CASE || type == CatalogType.PUBLIC_STEP) {
            sourceTreeList = recursiveQueryTree("ft_test_case", type, type, type, projectId, id);
        } else {
            throw new MyException("复制 tree 上某个type所有数据列表失败：type 未知，type: " + type + ", type.value: " + type.getValue());
        }

        // 递归到的元素不为空，递归复制
        if (sourceTreeList.size() > 0) {
            recursiveCopyTree(sourceTreeList, type, projectId, catalog.getId());
        }

        Tree tree = new Tree();
        tree.setProjectId(catalog.getProjectId().intValue());
        tree.setParentId(catalog.getParentId());
        tree.setId(catalog.getId());
        tree.setName(catalog.getName());
        tree.setKey("c-" + catalog.getId());
        tree.setSort(catalog.getSort());
        tree.setIsCatalog(true);
        return tree;
    }

    /**
     * 递归复制分类下的元素
     * @param sourceTreeList 原分类下的元素
     * @param catalogType 分类类型
     * @param projectId 项目 ID
     * @param parentId 新的父 ID
     */
    private void recursiveCopyTree(List<FullTree> sourceTreeList, CatalogType catalogType, Long projectId, Long parentId) {
        List<Long> oldElementList = new ArrayList<>();

        for (FullTree fullTree : sourceTreeList) {
            if (fullTree.getIsCatalog()) {
                // 复制分类
                Catalog catalog = catalogDao.selectById(fullTree.getId());
                if (catalog == null) {
                    continue;
                }
                catalog.setParentId(parentId);
                catalog.setId(null);
                catalog.setUpdatedAt(null);
                catalog.setCreatedAt(null);
                catalogDao.insert(catalog);

                recursiveCopyTree(fullTree.getChildren(), catalogType, projectId, catalog.getId()); // 递归复制子元素
            } else {
                oldElementList.add(fullTree.getId()); // 保存当前分类下的所有元素
            }
        }

        for (Long id : oldElementList) {
            if (catalogType == CatalogType.INTERFACE_HTTP || catalogType == CatalogType.INTERFACE_WEB_SOCKET || catalogType == CatalogType.INTERFACE_DATA_CHANNEL || catalogType == CatalogType.INTERFACE_DATABASE || catalogType == CatalogType.INTERFACE_SSH) {
                // 复制元素
                Api api = apiDao.selectById(catalogType.getValue(), id);
                if (api == null) {
                    continue;
                }
                api.setCatalogId(parentId);
                api.setId(null);
                api.setUpdatedAt(null);
                api.setCreatedAt(null);
                apiDao.insert(api);

                // 复制元素步骤
                for (ApiParam apiParam : api.getApiParams()) {
                    apiParam.setApiId(api.getId());
                    apiParam.setId(null);
                    apiParam.setUpdatedAt(null);
                    apiParam.setCreatedAt(null);
                }
                apiParamService.saveBatch(api.getApiParams());
            } else if (catalogType == CatalogType.TEST_CASE || catalogType == CatalogType.INTERFACE_TEST_CASE || catalogType == CatalogType.PUBLIC_STEP) {
                // 复制元素
                TestCase testCase = testCaseDao.selectById(id);
                if (testCase == null) {
                    continue;
                }
                testCase.setCatalogId(parentId);
                testCase.setId(null);
                testCase.setUpdatedAt(null);
                testCase.setCreatedAt(null);
                testCaseDao.insert(testCase);

                // 复制元素步骤
                for (Step step : testCase.getSteps()) {
                    step.setTestCaseId(testCase.getId());
                    step.setId(null);
                    step.setUpdatedAt(null);
                    step.setCreatedAt(null);
                }
                stepService.saveBatch(testCase.getSteps());
            } else {
                throw new MyException("复制 tree 上某个type所有数据列表失败：type 未知，type: " + catalogType + ", type.value: " + catalogType.getValue());
            }
        }
    }

    /**
     * 查找当前目录下的所有子元素 (子目录以及关联的用例)
     * @param eTableName
     * @param catalogType
     * @param elementType
     * @param projectId
     * @param id
     * @return
     */
    private List<Tree> findTreeContent(String eTableName, CatalogType catalogType, CatalogType elementType, Long projectId, Long id){
        List<Tree> treeList = new ArrayList<>();

        List<Tree> listTreeContent = null;
        if ("ft_api".equals(eTableName)) {
            listTreeContent = catalogDao.listApiTreeContent(catalogType, elementType, projectId, id);
        } else if ("ft_test_case".equals(eTableName)) {
            listTreeContent = catalogDao.listTestCaseTreeContent(catalogType, elementType, projectId, id);
        } else if ("pt_test_script".equals(eTableName)) {
            listTreeContent = catalogDao.listTestScriptTreeContent(catalogType, projectId, id);
        } else {
            throw new MyException("查询 tree 上某个type所有数据列表失败：eTableName 未知，eTableName: " + eTableName);
        }

        for(Tree tree: listTreeContent){
            if (tree.getIsCatalog() == true){
                findTreeContent(eTableName, catalogType, elementType, projectId, tree.getId());
            }
            treeList.add(tree);
        }
        return treeList;
    }

}
